/***************************************************************************
 *cr
 *cr            (C) Copyright 1995-2019 The Board of Trustees of the
 *cr                        University of Illinois
 *cr                         All Rights Reserved
 *cr
 ***************************************************************************/

/***************************************************************************
 * RCS INFORMATION:
 *
 *      $RCSfile: GraphicsFltkMenu.C,v $
 *      $Author: johns $        $Locker:  $             $State: Exp $
 *      $Revision: 1.163 $       $Date: 2019/01/17 21:20:59 $
 *
 ***************************************************************************
 * DESCRIPTION:
 *  generated by Fast Light User Interface Designer (fluid) version 1.0011
 ***************************************************************************/

#include "GraphicsFltkMenu.h"
#include "GraphicsFltkReps.h"
#include "Command.h"
#include "MoleculeList.h"
#include "Molecule.h"
#include "VMDApp.h"
#include "AtomRep.h"
#include "AtomColor.h"
#include "MaterialList.h"
#include "FL/forms.H"
#include "FL/Fl_Tabs.H"
#include "FL/Fl_Box.H"
#include "FL/Fl_Float_Input.H"
#include "SelectionBuilder.h"
#include "CmdMol.h"
#include "CommandQueue.h"
#include "VMDDisplayList.h" // for PBC #defines
#include "VolumetricData.h" 
#include "Scene.h"

// XXX enable new color tab, or not
// #define REPCOLORTAB 1

static const int widths[] = { 110, 100, 200, 0 };

static void add_rep_to_browser(DrawMolItem *d, Fl_Browser *browser, int replace) {
  char repbuf[20];
  const char *sel_str;
  strncpy(repbuf, d->atomRep->cmdStr, 20);
  repbuf[19] = '\0';
  char *firstspace = strchr(repbuf, ' ');
  if (firstspace) {
    *firstspace = '\0';
  } 
  
  if (!d->atomRep->is_volumetric()) 
    sel_str = d->atomSel->cmdStr;
  else 
    sel_str = "<volume>";
  
  char *buf = (char *)malloc(strlen(repbuf) + strlen(d->atomColor->cmdStr)
    + strlen(sel_str) + 20);
  const char *color = d->displayed() ? VMDMENU_REP_ACTIVE : VMDMENU_REP_INACTIVE;
  sprintf(buf,"%s%s\t%s%s\t%s%s", color, repbuf, color, d->atomColor->cmdStr, 
                                  color, sel_str);
  if (replace > 0)
    browser->text(replace, buf);
  else
    browser->add(buf);
  free(buf);
}


//
// Find the array index of a string that matches the name of the given menu
// item.  XXX Only checks leaf node menu names, not full pathnames currently
//
static int find_name_string_from_menuname(const char *mn, const char **names, int num) {
  // FLTK 1.1.4
  int i, strindex;
  for (strindex=-1, i=0; i < num; i++) {
    // find leaf menu name from full menu path
    const char *colstr;
    if ((colstr = strrchr(AtomColorMenuName[i], '/')) == NULL)
      colstr = AtomColorMenuName[i];
    else 
      colstr++;

    // compare leaf submenu item name against left color mode name
    if (!strcmp(colstr, mn)) {
      strindex=i;
      break;
    }
  }

  return strindex;
}


/// Fl_Hold_Browser subclass customized to allow double-clicking for
/// toggling graphical representations on and off.
class myBrowser : public Fl_Hold_Browser {
public:
  myBrowser(VMDApp *anApp, GraphicsFltkMenu *aMenu, 
      int ix, int iy, int iw, int ih) 
  : Fl_Hold_Browser(ix, iy, iw, ih), app(anApp), menu(aMenu) {}
  int handle(int type) {
    // toggle the highlighted rep on/off when double clicked...
    if (type == FL_PUSH && Fl::event_button() == FL_LEFT_MOUSE && 
        Fl::event_clicks()) {
      // ...but ignore double clicks on the scrollbar.  We distinguish these
      // events by checking that x value is inside the text bounding box.
      int bx, by, bw, bh;
      bbox(bx, by, bw, bh);
      if (Fl::event_x() < bx+bw) {
        Fl::event_is_click(0);
        int molid = app->molecule_id(menu->molindex);
        int rep = value()-1;
        app->molrep_show(molid, rep, !app->molrep_is_shown(molid, rep));
      }
      return 0;
    }
    return Fl_Hold_Browser::handle(type);
  }
private:
  VMDApp *app;
  GraphicsFltkMenu *menu;
};

/// Fl_Group subclass implementing controls for displaying 
/// periodic images for a graphical representation.
class PBCControls : public Fl_Group {
  Fl_Check_Button *plusx, *plusy, *plusz, *minusx, *minusy, *minusz, *self;
  Fl_Counter *ncounter;
public:
  PBCControls(int ix, int iy, int iw, int ih, const char *myname)
  : Fl_Group(ix, iy, iw, ih, myname) {
    Fl_Box *b = new Fl_Box(ix+30, iy+20, 190, 25, "Select periodic images to draw:");
    b->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
    plusx = new Fl_Check_Button(ix+50, iy+50, 70, 25, "+X");
    plusx->callback(cb, this);
    plusy = new Fl_Check_Button(ix+50, iy+80, 70, 25, "+Y");
    plusy->callback(cb, this);
    plusz = new Fl_Check_Button(ix+50, iy+110, 70, 25, "+Z");
    plusz->callback(cb, this);
    minusx = new Fl_Check_Button(ix+140, iy+50, 70, 25, "-X");
    minusx->callback(cb, this);
    minusy = new Fl_Check_Button(ix+140, iy+80, 70, 25, "-Y");
    minusy->callback(cb, this);
    minusz = new Fl_Check_Button(ix+140, iy+110, 70, 25, "-Z");
    minusz->callback(cb, this);
    self = new Fl_Check_Button(ix+50, iy+140, 70, 25, "Self");
    self->callback(cb, this);
    ncounter = new Fl_Counter(ix+50, iy+190, CTRWIDTH, CTRHEIGHT, 
        "Number of images");
    ncounter->precision(0);
    ncounter->minimum(1);
    ncounter->step(1);
    ncounter->lstep(5);
    ncounter->align(FL_ALIGN_TOP);
    ncounter->when(FL_WHEN_RELEASE);
    ncounter->callback(cb, this);
    end();
    update(0, 1);
  }
  static void cb(Fl_Widget *, void *v) { ((PBCControls *)v)->do_callback(); }

  // update checkboxes to reflect given pbc state
  void update(int pbc, int n) {
    plusx->value(pbc & PBC_X);
    plusy->value(pbc & PBC_Y);
    plusz->value(pbc & PBC_Z);
    minusx->value(pbc & PBC_OPX);
    minusy->value(pbc & PBC_OPY);
    minusz->value(pbc & PBC_OPZ);
    self->value(! (pbc & PBC_NOSELF));
    ncounter->value((double)n);
  }
  
  // Return current pbc state
  int state() const {
    int pbc = PBC_NONE;
    if (plusx->value()) pbc |= PBC_X;
    if (plusy->value()) pbc |= PBC_Y;
    if (plusz->value()) pbc |= PBC_Z;
    if (minusx->value()) pbc |= PBC_OPX;
    if (minusy->value()) pbc |= PBC_OPY;
    if (minusz->value()) pbc |= PBC_OPZ;
    if (!self->value()) pbc |= PBC_NOSELF;
    return pbc;
  }

  // Return number of periodic images
  int num_images() const { return (int)ncounter->value(); }
};

void GraphicsFltkMenu::pbc_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *self = (GraphicsFltkMenu *)v;
  PBCControls *control = (PBCControls *)w;
  self->set_pbc(control->state(), control->num_images());
}

void GraphicsFltkMenu::multiframeinput_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);

  if (!menu->app->molrep_set_drawframes(molid, menu->repindex, menu->create_multiframecmd()))
    fl_alert("The frame selection you typed could not be understood.");
}

static void autoupdate_cb(Fl_Widget *w, void *v) {
  Fl_Button *b = (Fl_Button *)w;
  ((GraphicsFltkMenu *)v)->set_autoupdate(b->value());
}
static void colorupdate_cb(Fl_Widget *w, void *v) {
  Fl_Button *b = (Fl_Button *)w;
  ((GraphicsFltkMenu *)v)->set_colorupdate(b->value());
}

static void colorscale_minmax_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *self = (GraphicsFltkMenu *)v;
  self->use_colorscale_minmax();
}
static void colorscale_auto_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *self = (GraphicsFltkMenu *)v;
  self->use_colorscale_auto();
}

GraphicsFltkMenu::GraphicsFltkMenu(VMDApp *vmdapp)
: VMDFltkMenu("graphics", "Graphical Representations", vmdapp) {
  app = vmdapp;
  molindex = -1;
  lastmolindex = -1;
  repindex = -1;

#if defined(REPCOLORTAB)
  int grwwidth = 365;
#else
  int grwwidth = 315;
#endif

  size(grwwidth - 2, 650);
  size_range(grwwidth, 650);
  { //Fl_Window* o = new Fl_Window(grwwidth, 541);
    //w = o;
    //o->user_data((void*)(this));
    { Fl_Box* o = repframe = new Fl_Box(5, 55, grwwidth - 10, 170);
      o->box(FL_ENGRAVED_FRAME);
    }
    Fl_Group *q = new Fl_Group(3, 5, grwwidth - 5, 45);
    q->resizable(NULL);
    { Fl_Choice* o = molchooser = new Fl_Choice(5, 25, grwwidth - 10, 25, "Selected Molecule");
      VMDFLTKTOOLTIP(o, "Select which molecule to create representations for")
      o->color(VMDMENU_CHOOSER_BG);
      o->selection_color(VMDMENU_CHOOSER_SEL);
      o->box(FL_THIN_UP_BOX);
      o->align(FL_ALIGN_TOP);
      o->callback(molchooser_cb, this);
    }
    q->end();

    Fl_Group *p = new Fl_Group(5, 60, grwwidth - 15, 50);
    p->resizable(NULL);
    createnewbutton = new Fl_Button(10, 65, 100, 25, "Create Rep");
#if defined(VMDMENU_WINDOW)
    createnewbutton->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    VMDFLTKTOOLTIP(createnewbutton, "Create new copy of selected representation")
    createnewbutton->callback(createnew_cb, this);
    { Fl_Button* o = deleterepbutton = new Fl_Button(grwwidth - 110, 65, 100, 25, "Delete Rep");
#if defined(VMDMENU_WINDOW)
      deleterepbutton->color(VMDMENU_WINDOW, FL_GRAY);
#endif
      VMDFLTKTOOLTIP(deleterepbutton, "Delete selected representation")
      o->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
      o->callback(deleterep_cb, this);
    }
    new Fl_Box(13,95,110,20, "Style");
    new Fl_Box(123,95,80,20, "Color");
    new Fl_Box(208,95,100,20, "Selection");
    p->end();

    { Fl_Browser* o = repbrowser = new myBrowser(app, this, 10, 115, grwwidth - 20, 100);
      VMDFLTKTOOLTIP(o, "Select representation to edit, double-click to toggle on/off")
      o->color(VMDMENU_BROWSER_BG);
      o->selection_color(VMDMENU_BROWSER_SEL);
      o->labeltype(FL_NO_LABEL);
      o->column_widths(widths);
      o->callback(repbrowser_cb, this);
      Fl_Group::current()->resizable(o);
      //o->resizable(o);
    }
    { Fl_Input* o = selectioninput = new Fl_Input(10, 245, grwwidth - 20, 30, "Selected Atoms");
      VMDFLTKTOOLTIP(o, "Select atoms to include in graphical representation")
      o->align(FL_ALIGN_TOP);
      o->when(FL_WHEN_ENTER_KEY);
      o->color(VMDMENU_VALUE_BG);
      o->selection_color(VMDMENU_VALUE_SEL);
      o->callback(selectioninput_cb, this);
    }

    Fl_Tabs *tabs = new Fl_Tabs(0, 280, grwwidth, 370);
#if defined(VMDMENU_WINDOW)
    tabs->color(VMDMENU_WINDOW, FL_GRAY);
    tabs->selection_color(VMDMENU_WINDOW);
#endif
    tabs->resizable(NULL);

    Fl_Group *stylegroup = new Fl_Group(0, 300, grwwidth, 350, "Draw style");
#if defined(VMDMENU_WINDOW)
    stylegroup->color(VMDMENU_WINDOW, FL_GRAY);
    stylegroup->selection_color(VMDMENU_WINDOW);
#endif

    { Fl_Choice* o = colorchooser = new Fl_Choice(10, 320, 120, 25, "Coloring Method");
      VMDFLTKTOOLTIP(o, "Select a coloring method for the molecular representation")
      o->color(VMDMENU_CHOOSER_BG);
      o->selection_color(VMDMENU_CHOOSER_SEL);
      o->box(FL_THIN_UP_BOX);
      o->align(FL_ALIGN_TOP);
      o->callback(colorchooser_cb, this);
    }
    { Fl_Choice* o = materialchooser = new Fl_Choice(180, 320, 120, 25, "Material");
      VMDFLTKTOOLTIP(o, "Select a material for shading the molecular representation")
      o->color(VMDMENU_CHOOSER_BG);
      o->selection_color(VMDMENU_CHOOSER_SEL);
      o->box(FL_THIN_UP_BOX);
      o->align(FL_ALIGN_TOP);
      o->callback(materialchooser_cb, this);
    }
    { Fl_Choice* o = stylechooser = new Fl_Choice(10, 375, 120, 25, "Drawing Method");
      VMDFLTKTOOLTIP(o, "Select a molecular representation drawing method")
      o->color(VMDMENU_CHOOSER_BG);
      o->selection_color(VMDMENU_CHOOSER_SEL);
      o->box(FL_THIN_UP_BOX);
      o->callback(repcontrol_cb, this);
      o->align(FL_ALIGN_TOP);
    }
    { Fl_Button* o = defaultbutton = new Fl_Button(180, 375, CTRWIDTH,CTRHEIGHT, "Default");
#if defined(VMDMENU_WINDOW)
      o->color(VMDMENU_WINDOW, FL_GRAY);
#endif
      VMDFLTKTOOLTIP(o, "Reset controls to defaults for this drawing method")
      o->callback(default_cb, this);
    }

    // color index chooser, hide initially only shown for ColorID method
    { Fl_Choice* o = colindexchooser = new Fl_Choice(130, 320, 50, 25);
      o->color(VMDMENU_CHOOSER_BG, VMDMENU_CHOOSER_SEL);
      for (int c=0; c<REGCLRS; c++) {
        char buf[128];
        sprintf(buf, "%d     %s", c, app->scene->color_name(c));
        o->add(buf);
      }
      o->value(0);
      o->callback(colorchooser_cb, this);
      o->hide();
      o->deactivate();
    }

    // volume index chooser, hide initially only shown for Volume method
    { Fl_Choice* o = volindexchooser = new Fl_Choice(130, 320, 50, 25);
      o->color(VMDMENU_CHOOSER_BG, VMDMENU_CHOOSER_SEL);
      o->value(0);
      o->callback(colorchooser_cb, this);
      o->hide();
      o->deactivate();
    }

    { Fl_Check_Button* o = applyautobutton = new Fl_Check_Button(35, 620, 240, 25, "Apply Changes Automatically");
#if defined(VMDMENU_WINDOW)
      o->color(VMDMENU_WINDOW, FL_GRAY);
#endif
      VMDFLTKTOOLTIP(applyautobutton, "Apply changes automatically as they are made")
      o->down_box(FL_DIAMOND_DOWN_BOX);
      o->align(132|FL_ALIGN_INSIDE);
      o->value(1);
    }
    applybutton = new Fl_Button(255, 620, 45, 25, "Apply");
    VMDFLTKTOOLTIP(applybutton, "Apply representation changes now")
    applybutton->callback(apply_cb, this);

    repcontrols.add_name("Lines",new GraphicsFltkRepLines(repcontrol_cb, this));
    repcontrols.add_name("Bonds",new GraphicsFltkRepBonds(repcontrol_cb, this));
    repcontrols.add_name("DynamicBonds",new GraphicsFltkRepDynamicBonds(repcontrol_cb, this));
    repcontrols.add_name("HBonds",new GraphicsFltkRepHBonds(repcontrol_cb, this));

    repcontrols.add_name("Points",new GraphicsFltkRepPoints(repcontrol_cb, this));
    repcontrols.add_name("VDW",new GraphicsFltkRepVDW(repcontrol_cb, this));
    repcontrols.add_name("CPK",new GraphicsFltkRepCPK(repcontrol_cb, this));
    repcontrols.add_name("Licorice",new GraphicsFltkRepLicorice(repcontrol_cb, this));
    repcontrols.add_name("Trace",new GraphicsFltkRepTrace(repcontrol_cb, this));
    repcontrols.add_name("Tube",new GraphicsFltkRepTube(repcontrol_cb, this));
    repcontrols.add_name("Ribbons",new GraphicsFltkRepRibbons(repcontrol_cb, this));
    repcontrols.add_name("NewRibbons",new GraphicsFltkRepNewRibbons(repcontrol_cb, this));
    repcontrols.add_name("Cartoon",new GraphicsFltkRepCartoon(repcontrol_cb, this));
    repcontrols.add_name("NewCartoon",new GraphicsFltkRepNewCartoon(repcontrol_cb, this));

#ifdef VMDWITHCARBS
    repcontrols.add_name("PaperChain",new GraphicsFltkRepPaperChain(repcontrol_cb, this));
    repcontrols.add_name("Twister",new GraphicsFltkRepTwister(repcontrol_cb, this));
#endif
#ifdef VMDPOLYHEDRA
    repcontrols.add_name("Polyhedra",new GraphicsFltkRepPolyhedra(repcontrol_cb, this));
#endif
#ifdef VMDQUICKSURF
    // special callback data structure to pass multiple pointers 
    qsurfcbdata = new quicksurf_cbdata;
    qsurfcbdata->self = this;
    qsurfcbdata->qsurfrep = new GraphicsFltkRepQuickSurf(quicksurfrepcontrol_cb, qsurfcbdata);
    repcontrols.add_name("QuickSurf", qsurfcbdata->qsurfrep);
#endif
    repcontrols.add_name("MSMS",new GraphicsFltkRepMSMS(repcontrol_cb, this));
#ifdef VMDNANOSHAPER
    repcontrols.add_name("NanoShaper",new GraphicsFltkRepNanoShaper(repcontrol_cb, this));
#endif
    repcontrols.add_name("Surf",new GraphicsFltkRepSurf(repcontrol_cb, this));
    repcontrols.add_name("VolumeSlice",new GraphicsFltkRepVolumeSlice(repcontrol_cb, this));
   
    // special callback data structure to pass multiple pointers 
    isosurfcbdata = new isosurface_cbdata;
    isosurfcbdata->self = this;
    isosurfcbdata->isorep = new GraphicsFltkRepIsosurface(isosurfacerepcontrol_cb, isosurfcbdata);
    repcontrols.add_name("Isosurface", isosurfcbdata->isorep);
    repcontrols.add_name("FieldLines", new GraphicsFltkRepFieldLines(repcontrol_cb, this));

    // special callback data structure to pass multiple pointers 
    orbcbdata = new orbital_cbdata;
    orbcbdata->self = this;
    orbcbdata->orbrep = new GraphicsFltkRepOrbital(orbitalrepcontrol_cb, orbcbdata);
    repcontrols.add_name("Orbital", orbcbdata->orbrep);

    repcontrols.add_name("Beads",new GraphicsFltkRepBeads(repcontrol_cb, this));
    repcontrols.add_name("Dotted",new GraphicsFltkRepDotted(repcontrol_cb, this));
    repcontrols.add_name("Solvent",new GraphicsFltkRepSolvent(repcontrol_cb, this));
#ifdef VMDLATTICECUBES
    repcontrols.add_name("LatticeCubes",new GraphicsFltkRepLatticeCubes(repcontrol_cb, this));
#endif

    stylegroup->end();
    selbuilder = new SelectionBuilder(0, 300, this, selectioninput, app->atomSelParser);
#if defined(VMDMENU_WINDOW)
    selbuilder->color(VMDMENU_WINDOW, FL_GRAY);
    selbuilder->selection_color(VMDMENU_WINDOW);
#endif
    selbuilder->end();

#if defined(REPCOLORTAB)
    /// XXX new color tab
    /// will eventually hold:
    ///   - controls for two-sided coloring
    ///   - per rep color scale setting
    ///   - color scale data range
    ///   - linear/log/etc color mapping function
    ///   - histogram/color ramp editor launch button?
    Fl_Group *colorgroup = new Fl_Group(0, 300, grwwidth, 295, "Color");

    Fl_Box *b2 = new Fl_Box(20, 380, 200, 25, "Color Scale Data Range:");
    VMDFLTKTOOLTIP(b2, "Customize mapping of data to color scale by entering min and max values")
    b2->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
    colorscale_min = new Fl_Float_Input(20, 405, 70, 20);
    colorscale_min->color(VMDMENU_VALUE_BG, VMDMENU_VALUE_SEL);
    colorscale_min->when(FL_WHEN_ENTER_KEY);
    colorscale_min->callback(colorscale_minmax_cb, this);

    colorscale_max = new Fl_Float_Input(95, 405, 70, 20);
    colorscale_max->color(VMDMENU_VALUE_BG, VMDMENU_VALUE_SEL);
    colorscale_max->when(FL_WHEN_ENTER_KEY);
    colorscale_max->callback(colorscale_minmax_cb, this);

    Fl_Button *b3 = new Fl_Button(170, 405, 50, 20, "Set");
#if defined(VMDMENU_WINDOW)
    b3->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    b3->callback(colorscale_minmax_cb, this);

    Fl_Button *b4 = new Fl_Button(225, 405, 80, 20, "Autoscale");
#if defined(VMDMENU_WINDOW)
    b4->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    b4->callback(colorscale_auto_cb, this);

    colorgroup->end();
#endif /* REPCOLORTAB */

    Fl_Group *animationgroup = new Fl_Group(0, 300, grwwidth, 295, "Trajectory");
#if defined(VMDMENU_WINDOW)
    animationgroup->color(VMDMENU_WINDOW, FL_GRAY);
    animationgroup->selection_color(VMDMENU_WINDOW);
#endif

    autoupdatebutton = new Fl_Check_Button(20, 320, 230, 25, "Update Selection Every Frame");
    autoupdatebutton->callback(autoupdate_cb, this);
    autoupdatebutton->down_box(FL_DIAMOND_DOWN_BOX);
    autoupdatebutton->align(132 | FL_ALIGN_INSIDE);
    autoupdatebutton->value(0);

    colorupdatebutton = new Fl_Check_Button(20, 350, 230, 25, "Update Color Every Frame");
    colorupdatebutton->callback(colorupdate_cb, this);
    colorupdatebutton->down_box(FL_DIAMOND_DOWN_BOX);
    colorupdatebutton->align(132 | FL_ALIGN_INSIDE);
    colorupdatebutton->value(0);

#if !defined(REPCOLORTAB)
    Fl_Box *b2 = new Fl_Box(20, 380, 200, 25, "Color Scale Data Range:");
    VMDFLTKTOOLTIP(b2, "Customize mapping of data to color scale by entering min and max values")
    b2->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
    colorscale_min = new Fl_Float_Input(20, 405, 70, 20);
    colorscale_min->color(VMDMENU_VALUE_BG, VMDMENU_VALUE_SEL);
    colorscale_min->when(FL_WHEN_ENTER_KEY);
    colorscale_min->callback(colorscale_minmax_cb, this);
    colorscale_max = new Fl_Float_Input(95, 405, 70, 20);
    colorscale_max->color(VMDMENU_VALUE_BG, VMDMENU_VALUE_SEL);
    colorscale_max->when(FL_WHEN_ENTER_KEY);
    colorscale_max->callback(colorscale_minmax_cb, this);
    Fl_Button *b3 = new Fl_Button(170, 405, 50, 20, "Set");
#if defined(VMDMENU_WINDOW)
    b3->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    b3->callback(colorscale_minmax_cb, this);
    Fl_Button *b4 = new Fl_Button(225, 405, 80, 20, "Autoscale");
#if defined(VMDMENU_WINDOW)
    b4->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    b4->callback(colorscale_auto_cb, this);
#endif

    new Fl_Box(10, 440, 250, 25, "Draw Multiple Frames: (now, b:e, b:s:e, comma separated)");
    multiframeinput = new Fl_Input(20, 465, 250, 20);
    multiframeinput->align(FL_ALIGN_TOP);
    multiframeinput->when(FL_WHEN_ENTER_KEY);
    multiframeinput->color(VMDMENU_VALUE_BG);
    multiframeinput->selection_color(VMDMENU_VALUE_SEL);
    multiframeinput->callback(multiframeinput_cb, this);
    multiframeinput->value("now");
    VMDFLTKTOOLTIP(multiframeinput, "Select the frames to be drawn simultaneously")

    Fl_Box *b1 = new Fl_Box(30, 490, 300, 25, 
        "Trajectory Smoothing Window Size:");
    b1->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
    smoothcounter = new ResolutionCounter(70, 515, NULL);
    smoothcounter->size(smoothcounter->w(), 20);
    smoothcounter->minimum(0);
    smoothcounter->callback(smooth_cb, this);

    animationgroup->end();

    pbcControls = new PBCControls(0, 300, grwwidth, 295, "Periodic");
#if defined(VMDMENU_WINDOW)
    pbcControls->color(VMDMENU_WINDOW, FL_GRAY);
    pbcControls->selection_color(VMDMENU_WINDOW);
#endif
    pbcControls->callback(pbc_cb, this);

    tabs->end();
    Fl_Window::end();

    for (int j=0; j<repcontrols.num(); j++) {
      repcontrols.data(j)->reset();
      repcontrols.data(j)->hide();
    }
  }

  command_wanted(Command::MOL_NEW);
  command_wanted(Command::MOL_DEL);
  command_wanted(Command::MOL_ON);
  command_wanted(Command::MOL_RENAME);
  command_wanted(Command::MOL_VOLUME);
  command_wanted(Command::MOL_ADDREP);
  command_wanted(Command::MOL_DELREP);
  command_wanted(Command::MOL_MODREP);
  command_wanted(Command::MOL_MODREPITEM);
  command_wanted(Command::MOL_SHOWPERIODIC);
  command_wanted(Command::MOL_NUMPERIODIC);
  command_wanted(Command::MOL_DRAWFRAMES);
  command_wanted(Command::MOL_SCALEMINMAX);
  command_wanted(Command::MOL_SMOOTHREP);
  command_wanted(Command::MOL_SHOWREP);
  command_wanted(Command::MATERIAL_RENAME);
  command_wanted(Command::MATERIAL_ADD);
  command_wanted(Command::MATERIAL_DELETE);
  command_wanted(Command::MOL_REPSELUPDATE);
  command_wanted(Command::MOL_REPCOLORUPDATE);
  command_wanted(Command::ATOMSEL_ADDMACRO);
  command_wanted(Command::ATOMSEL_DELMACRO);

  init_colorchooser();
  init_materialchooser();
  init_stylechooser();

  selbuilder->update_macrobrowser();
}

GraphicsFltkMenu::~GraphicsFltkMenu() {
  delete isosurfcbdata;
  delete orbcbdata;
  delete qsurfcbdata;
}

int GraphicsFltkMenu::auto_update() const {
  return applyautobutton->value();
}

void GraphicsFltkMenu::init_colorchooser() {
  colorchooser->clear();

  // Add "pretty" menu names for use in the GUI, which also allows us
  // to add submenus and other organizational structure to the menu.
  // The cost of this is that we cannot compare indices between menus and
  // coloring modes, we have to match strings instead. 
  for (int j=0; j<AtomColor::TOTAL; j++) 
    colorchooser->add(AtomColorMenuName[j]); 

  colorchooser->value(0); // force init to first menu item

  // set to "Name" coloring method by default
  set_chooser_from_string(AtomColorMenuName[AtomColor::NAME], colorchooser);
}

void GraphicsFltkMenu::init_materialchooser() {
  materialchooser->clear();
  for (int j=0; j<app->materialList->num(); j++) 
    materialchooser->add(app->materialList->material_name(j)); 
  materialchooser->value(0);
}

void GraphicsFltkMenu::init_stylechooser() {
  stylechooser->clear();
  for (int j=0; j<AtomRep::TOTAL; j++) 
    stylechooser->add(AtomRepInfo[j].name); 
  stylechooser->value(0);
  show_repcontrols();
}

void GraphicsFltkMenu::isosurfacerepcontrol_cb(Fl_Widget *w, void *v) {
  isosurface_cbdata *cbdata = (isosurface_cbdata *) v;
  GraphicsFltkMenu *menu = cbdata->self;
  GraphicsFltkRepIsosurface *iso = cbdata->isorep; 

  if (!strcmp(w->label(), "Drawing Method")) 
    menu->show_repcontrols();
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);

  if (Fl::event_state(FL_BUTTON1 | FL_BUTTON2 | FL_BUTTON3)) {
    if (Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT | FL_META | FL_BUTTON2 | FL_BUTTON3)) { 
      // drag callback, with a modifier key pressed
      // this allows the user to retain full resolution if they really want
      iso->set_grid_stepsize(1);
    } else {
      // drag callback, use a larger step size while dragging so that the
      // isosurface computes more quickly
      iso->set_grid_stepsize(2);
    }
  } else {
    // release callback
    iso->set_grid_stepsize(1);
  }
  menu->app->molrep_set_style(molid, menu->repindex, menu->create_repcmd());
}


void GraphicsFltkMenu::orbitalrepcontrol_cb(Fl_Widget *w, void *v) {
  orbital_cbdata *cbdata = (orbital_cbdata *) v;
  GraphicsFltkMenu *menu = cbdata->self;
  GraphicsFltkRepOrbital *orb = cbdata->orbrep; 

  if (!strcmp(w->label(), "Drawing Method")) 
    menu->show_repcontrols();

  // regenerate the excitation list whenever the wavefunction type is changed
  if (!strcmp(w->label(), "Wavefunction Type")) {
    orb->regen_excitationlist();
  }

  // regenerate the orbital list whenever wavefunction type, spin, or 
  // excitation selections are changed.
  if (!strcmp(w->label(), "Wavefunction Type") ||
      !strcmp(w->label(), "Spin") ||
      !strcmp(w->label(), "Excitation")) {
    orb->regen_orbitallist();
  }    

  // regenerate the orbital list whenever the user selects a different
  // range of orbitals using the "OrbList" counter
  if (!strcmp(w->label(), "OrbList")) {
    orb->regen_orbitallist(-1);
  }    

  // if the menu isn't set to apply changes automatically, then
  // we do nothing further and simply return
  if (!menu->auto_update()) return;

  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);

  if (Fl::event_state(FL_BUTTON1 | FL_BUTTON2 | FL_BUTTON3)) {
    if (Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT | FL_META | FL_BUTTON2 | FL_BUTTON3)) { 
      // drag callback, with a modifier key pressed
      // this allows the user to retain full resolution if they really want
      orb->set_grid_stepsize(1);
    } else {
      // drag callback, use a larger step size while dragging so that the
      // isosurface computes more quickly
      orb->set_grid_stepsize(2);
    }
  } else {
    // release callback
    orb->set_grid_stepsize(1);
  }
  menu->app->molrep_set_style(molid, menu->repindex, menu->create_repcmd());
}


void GraphicsFltkMenu::quicksurfrepcontrol_cb(Fl_Widget *w, void *v) {
  quicksurf_cbdata *cbdata = (quicksurf_cbdata *) v;
  GraphicsFltkMenu *menu = cbdata->self;
  GraphicsFltkRepQuickSurf *qsurf = cbdata->qsurfrep; 

  if (!strcmp(w->label(), "Drawing Method")) 
    menu->show_repcontrols();

  // if the menu isn't set to apply changes automatically, then
  // we do nothing further and simply return
  if (!menu->auto_update()) return;

  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);

  if (Fl::event_state(FL_BUTTON1 | FL_BUTTON2 | FL_BUTTON3)) {
    if (Fl::event_state(FL_SHIFT | FL_CTRL | FL_ALT | FL_META | FL_BUTTON2 | FL_BUTTON3)) {
      // drag callback, with a modifier key pressed
      // this allows the user to retain full resolution if they really want
      qsurf->set_gridspacing_multiplier(1.0f);
    } else {
      // drag callback, use a larger grid spacing while dragging so that the
      // density map computes more quickly
      qsurf->set_gridspacing_multiplier(2.0f);
    }
  } else {
    // release callback
    qsurf->set_gridspacing_multiplier(1.0f);
  }

  // regenerate the parameters when the resolution slider is changed
  if (!strcmp(w->label(), "Resolution")) {
    qsurf->resolution_changed();
  }

  menu->app->molrep_set_style(molid, menu->repindex, menu->create_repcmd());
}


void GraphicsFltkMenu::molchooser_cb(Fl_Widget *w, void *v) {
  Fl_Choice *choice = (Fl_Choice *)w;
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  menu->molindex = choice->value();
  menu->update_molchooser();
}

void GraphicsFltkMenu::repbrowser_cb(Fl_Widget *w, void *v) {
  Fl_Hold_Browser *b = (Fl_Hold_Browser *)w;
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  menu->repindex = b->value()-1;
  menu->update_rep();
}
 
void GraphicsFltkMenu::repcontrol_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  if (!strcmp(w->label(), "Drawing Method")) 
    menu->show_repcontrols();
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molrep_set_style(molid, menu->repindex, menu->create_repcmd());
}

void GraphicsFltkMenu::colorchooser_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  menu->colindex_visible();
  menu->volindex_visible();
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molrep_set_color(molid, menu->repindex, menu->create_colorcmd());
}
  
void GraphicsFltkMenu::selectioninput_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);
  if (!menu->app->molrep_set_selection(molid, menu->repindex, menu->create_selcmd())) 
    fl_alert("The atom selection you typed could not be understood.");
}

void GraphicsFltkMenu::materialchooser_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  if (!menu->auto_update()) return;
  if (menu->molindex < 0 || menu->repindex < 0) return;
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molrep_set_material(molid, menu->repindex, menu->create_matcmd());
}
    
void GraphicsFltkMenu::createnew_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  
  menu->app->molecule_set_color(menu->create_colorcmd());
  menu->app->molecule_set_style(menu->create_repcmd());
  menu->app->molecule_set_selection(menu->create_selcmd());
  menu->app->molecule_set_material(menu->create_matcmd());
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molecule_addrep(molid);
}
  
void GraphicsFltkMenu::deleterep_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molrep_delete(molid, menu->repindex); 
}

void GraphicsFltkMenu::apply_cb(Fl_Widget *w, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  if (menu->molindex < 0 || menu->repindex < 0) return;

  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molecule_set_color(menu->create_colorcmd());
  menu->app->molecule_set_style(menu->create_repcmd());
  menu->app->molecule_set_selection(menu->create_selcmd());
  menu->app->molecule_set_material(menu->create_matcmd());
  menu->app->molecule_modrep(molid, menu->repindex);
}

void GraphicsFltkMenu::default_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  menu->reset_rep();
  GraphicsFltkMenu::apply_cb(0, menu);
}

void GraphicsFltkMenu::smooth_cb(Fl_Widget *, void *v) {
  GraphicsFltkMenu *menu = (GraphicsFltkMenu *)v;
  int molid = menu->app->molecule_id(menu->molindex);
  menu->app->molrep_set_smoothing(molid, menu->repindex, 
      (int)menu->smoothcounter->value());
}

void GraphicsFltkMenu::use_colorscale_minmax() {
  if (molindex >= 0 && repindex >= 0) {
    int molid = app->molecule_id(molindex);
    float min = (float) atof(colorscale_min->value());
    float max = (float) atof(colorscale_max->value());
    if (!app->molrep_set_scaleminmax(molid, repindex, min, max)) {
      fl_alert("Could not set color scale data range with values %s-%s",
          colorscale_min->value(), colorscale_max->value());
    }
  }
}

void GraphicsFltkMenu::use_colorscale_auto() {
  if (molindex >= 0 && repindex >= 0) {
    int molid = app->molecule_id(molindex);
    if (!app->molrep_reset_scaleminmax(molid, repindex)) {
      fl_alert("Could not autoscale color scale data range.");
    } else { // update Min/Max text fields
      float min=0, max=0;
      char buf[128]; // must hold potentially huge FP numbers
      app->molrep_get_scaleminmax(molid, repindex, &min, &max);
      sprintf(buf, "%3.2f", min);
      colorscale_min->value(buf);
      sprintf(buf, "%3.2f", max);
      colorscale_max->value(buf);
    }
  }
}

void GraphicsFltkMenu::update_selection(const char *s) {
  if (s) {
    selectioninput->value(s);
    selectioninput->do_callback();
  } else {
    int id = app->molecule_id(molindex);
    const char *txt = app->molrep_get_selection(id, repindex);
    if (txt) {
      selectioninput->value(s);
// XXX this causes the user to see an error box for no good reason
//      selectioninput->do_callback();
    }
  }
}
 
const char *GraphicsFltkMenu::selectiontext() const {
  return selectioninput->value();
}

void GraphicsFltkMenu::reset_rep() {
  int typecode = repcontrols.typecode(stylechooser->text());
  // assert(typecode >= 0);
  repcontrols.data(typecode)->reset();
}
 
void GraphicsFltkMenu::update_molchooser() {
  fill_fltk_molchooser(molchooser, app, NULL);
  volindex_update();

  if (molindex >= 0) {
    Molecule *m = app->moleculeList->molecule(molindex);

#if 0
    // disable volumetric coloring/reps if no volume data loaded
    int volmode = 0; // no flags by default, item active
    if (m->num_volume_data() < 1) {
      volmode = FL_MENU_INACTIVE;
    }
    colorchooser->mode(find_menu_from_string(AtomColorName[AtomColor::VOLUME], colorchooser->menu()), volmode);
    stylechooser->mode(AtomRep::VOLSLICE, volmode);
    stylechooser->mode(AtomRep::ISOSURFACE, volmode);
    stylechooser->mode(AtomRep::FIELDLINES, volmode);
#endif

    molchooser->value(molindex);
    for (int k=0; k<repcontrols.num(); k++) {
      if (repcontrols.data(k)->is_volumetric()) { 
        GraphicsFltkRepVolumetric *rep = 
          (GraphicsFltkRepVolumetric *)repcontrols.data(k);
        rep->dataset_clear();
        for (int j=0; j<m->num_volume_data(); j++) {
          VolumetricData *data = m->modify_volume_data(j);
          float datamin, datamax;
          data->datarange(datamin, datamax);
          rep->dataset_append(data->name, datamin, datamax);
        }
      }

      // deal with Orbital reps
      if (repcontrols.data(k)->is_orbital()) { 
        GraphicsFltkRepOrbital *rep = 
          (GraphicsFltkRepOrbital *)repcontrols.data(k);

        // update the list of available wavefunction types
        rep->regen_wavefunctypes();

        // regenerate the orbital chooser contents based on the
        // currently selected wavefunction type, spin, and excitation
        rep->regen_orbitallist();
      }
    }
  } else {
    molchooser->redraw();
  }
  selbuilder->use_molecule(app->moleculeList->molecule(molindex));
  app->highlighted_molid = app->molecule_id(molindex);
  update_repindex();
  update_repbrowser();
}

void GraphicsFltkMenu::update_repbrowser(bool remember_position) {
  int vposition =  repbrowser->position();
  repbrowser->clear();
  Molecule *mol = app->moleculeList->molecule(molindex);
  if (!mol) return;

  for (int j=0; j<mol->components(); j++) {
    DrawMolItem *d = mol->component(j);
    add_rep_to_browser(d, repbrowser, 0);
  }
  // assert(repindex < repbrowser->size());
  if (repindex >= 0) {
    repbrowser->select(repindex+1);
    if (remember_position) 
      repbrowser->position(vposition);
  }
  update_rep();
}
 
void GraphicsFltkMenu::update_rep() {
  if (molindex >= 0 && repindex >= 0) {
    Molecule *m = app->moleculeList->molecule(molindex);
    DrawMolItem *d = m->component(repindex);

    // colors
    AtomColor *ac = d->atomColor;

    // find the named menu item, set it active if a match is found
    set_chooser_from_string(AtomColorMenuName[ac->method()], colorchooser);

    // volume texturing
    volindex_visible(); 
    if (ac->method() == AtomColor::VOLUME){
      volindexchooser->value(ac->volume_index());
    } 

    // coloring method
    colindex_visible(); 
    if (ac->method() == AtomColor::COLORID){
      colindexchooser->value(ac->color_index());
    }

    colorupdatebutton->value(ac->do_update);
    float min=0, max=0;
    char buf[128]; // must hold potentially huge FP numbers
    ac->get_colorscale_minmax(&min, &max);
    sprintf(buf, "%3.2f", min);
    colorscale_min->value(buf);
    sprintf(buf, "%3.2f", max);
    colorscale_max->value(buf);

    materialchooser->value(d->curr_material());
    AtomRep *ar = d->atomRep;
    if (stylechooser->value() != ar->method()) {
      stylechooser->value(ar->method());
      show_repcontrols();
    }
    repcontrols.data(stylechooser->text())->set_values(ar);
    AtomSel *as = d->atomSel;
    selectioninput->value(as->cmdStr);
    if (ar->is_volumetric()) selectioninput->deactivate();
    else selectioninput->activate();
    selbuilder->set_selection(as->cmdStr);
    autoupdatebutton->value(as->do_update);
    smoothcounter->value((double)d->get_smoothing());
    multiframeinput->value(d->get_drawframes());
    update_pbc();
  }
  app->highlighted_rep = repindex;
}
   
// update the pbc controls for the current rep
void GraphicsFltkMenu::update_pbc() {
  if (molindex >= 0 && repindex >= 0) {
    Molecule *m = app->moleculeList->molecule(molindex);
    const DrawMolItem *d = m->component(repindex);
    int pbc = d->get_pbc();
    int n = d->get_pbc_images();
    pbcControls->update(pbc, n);
  }
}

// set pbc values for the current rep, if any
void GraphicsFltkMenu::set_pbc(int pbc, int n) {
  if (molindex >= 0 && repindex >= 0) {
    Molecule *m = app->moleculeList->molecule(molindex);
    int id = m->id();
    app->molrep_set_pbc(id, repindex, pbc);
    app->molrep_set_pbc_images(id, repindex, n);
  }
}

void GraphicsFltkMenu::set_autoupdate(int on) {
  if (molindex >= 0 && repindex >= 0) {
    int id = app->molecule_id(molindex);
    app->molrep_set_selupdate(id, repindex, on);
  }
}

void GraphicsFltkMenu::set_colorupdate(int on) {
  if (molindex >= 0 && repindex >= 0) {
    int id = app->molecule_id(molindex);
    app->molrep_set_colorupdate(id, repindex, on);
  }
}

void GraphicsFltkMenu::update_repindex() {
  // auto-grow the rep index array as needed, initializing reps to -1
  while (molactiverep.num() <= lastmolindex) {
    molactiverep.append(-1);
  }

  // save active repindex for last molecule accessed before we update
  Molecule *oldmol = app->moleculeList->molecule(lastmolindex);
  if (oldmol) {
    molactiverep[lastmolindex] = repindex;
  } 

  // see if new molecule exists
  Molecule *mol = app->moleculeList->molecule(molindex);
  if (!mol) {
    repindex = -1;
    return;
  }
 
  // auto-grow the rep index array as needed, initializing reps to -1
  while (molactiverep.num() <= molindex) {
    molactiverep.append(-1);
  }

  // retrieve previous rep index for this molecule
  repindex = molactiverep[molindex];
  lastmolindex = molindex;

  // bounds check the repindex before returning
  int n = mol->components();
  if (n > 0 && repindex == -1) {
    repindex = 0;
    return;
  }
  if (repindex >= n) {
    repindex = n-1;
  }
}
    
void GraphicsFltkMenu::show_repcontrols() {
  for (int j=0; j<repcontrols.num(); j++) 
    repcontrols.data(j)->hide();

  int typecode = repcontrols.typecode(stylechooser->text());
  // assert(typecode >= 0);
  repcontrols.data(typecode)->show();
  redraw();
}

char *GraphicsFltkMenu::create_repcmd() {
  int typecode = repcontrols.typecode(stylechooser->text());
  // assert(typecode >= 0);
  return (char *)repcontrols.data(typecode)->repcmd();
}

char *GraphicsFltkMenu::create_colorcmd() {
  // find the coloring method mode that matches the currently selected
  // colorchooser item name
  int colorval = find_name_string_from_menuname(colorchooser->text(), AtomColorMenuName, AtomColor::TOTAL);

  // if we got a valid color name index, set the color command text
  if (colorval >= 0) {
    if (colorval == AtomColor::COLORID) {
      sprintf(colorcmdbuf, "%s %d", AtomColorName[colorval],
              colindexchooser->value());
    } else if (colorval == AtomColor::VOLUME) {
      sprintf(colorcmdbuf, "%s %d", AtomColorName[colorval],
              volindexchooser->value());
    } else {
      strcpy(colorcmdbuf, AtomColorName[colorval]);
    }
  } else {
    colorcmdbuf[0] = '\0';
  }

  return colorcmdbuf;
}
        
char *GraphicsFltkMenu::create_selcmd() {
  return (char *)(selectioninput->value()); 
}

char *GraphicsFltkMenu::create_matcmd() {
  return (char *)(materialchooser->text());
}

char *GraphicsFltkMenu::create_multiframecmd() {
  return (char *)(multiframeinput->value()); 
}

void GraphicsFltkMenu::colindex_visible() {
  // compare menu names to check for a match, since indices aren't correlated
  if (!strcmp(AtomColorMenuName[AtomColor::COLORID], colorchooser->text())) {
    colindexchooser->show();
    colindexchooser->activate();
  } else {
    colindexchooser->hide();
    colindexchooser->deactivate();
  }
}

void GraphicsFltkMenu::volindex_visible() {
  // compare menu names to check for a match, since indices aren't correlated
  if (!strcmp(AtomColorMenuName[AtomColor::VOLUME], colorchooser->text())) {
    volindexchooser->show();
    volindexchooser->activate();
  } else {
    volindexchooser->hide();
    volindexchooser->deactivate();
  }
}

void GraphicsFltkMenu::volindex_update() {
  int newvolid = volindexchooser->value();
  volindexchooser->clear();

  // XXX This code makes the short-term assumption that volume ID's are
  // sequentially numbered with no gaps, presently true but ultimately
  // this will break once volumes can be deleted.  Needs to use an extra
  // GUI index to volume index mapping array to be correct long-term, much
  // like we do with molecules.
  if (molindex >= 0) {
    Molecule *m = app->moleculeList->molecule(molindex);
    for (int j=0; j<m->num_volume_data(); j++) {
      char volnamebuf[80];
      const VolumetricData *data = m->get_volume_data(j);

      memset(volnamebuf, 0, sizeof(volnamebuf));
      sprintf(volnamebuf, "%d: ", j);
      strncpy(volnamebuf+strlen(volnamebuf), data->name, (sizeof(volnamebuf) - strlen(volnamebuf) - 2));

      // Fltk doesn't allow adding a menu item with the same name as
      // an existing item, so we use replace, which also avoids 
      // problems with the escape characters interpreted by add()
      int ind = volindexchooser->add("foobar");
      volindexchooser->replace(ind, volnamebuf);
    }

    // range-check the old volume index against what we have now
    // if the old index is now out of range, set it to zero.
    if ((newvolid < 0) || (newvolid >= m->num_volume_data()))
      newvolid = 0;
    volindexchooser->value(newvolid);
  }
}

int GraphicsFltkMenu::act_on_command(int type, Command *cmd) {
  if (type == Command::MOL_NEW || type == Command::MOL_DEL ||
      type == Command::MOL_VOLUME || type == Command::MOL_RENAME) {
    Molecule *mol = app->moleculeList->top();
    if (mol)
      molindex = app->moleculeList->mol_index_from_id(mol->id());
    else
      molindex = -1;
    update_repindex();
    update_molchooser();
  } else if (type == Command::MOL_ON) {
    update_molchooser();

  } else if (type == Command::MOL_ADDREP || type == Command::MOL_DELREP ||
             type == Command::MOL_MODREP || type == Command::MOL_MODREPITEM ||
             type == Command::MOL_DRAWFRAMES ||
             type == Command::MOL_SCALEMINMAX ||
             type == Command::MOL_SHOWREP ||
             type == Command::MOL_SMOOTHREP) {
    if (type == Command::MOL_DELREP) {
      int delrep = ((CmdMolDeleteRep *)cmd)->repn;
      if (delrep < repindex) --repindex;
    } else if (type == Command::MOL_ADDREP) {
      // make update_repindex highlight the last rep
      repindex = 99999; 
    }

    // Keep the browser line at the same position unless we're adding
    // or deleting representations.
    bool remember_position = true;
    if (type == Command::MOL_ADDREP || type == Command::MOL_DELREP) 
      remember_position = false;
    update_repindex();
    update_repbrowser(remember_position);
  } else if (type == Command::MATERIAL_RENAME
          || type == Command::MATERIAL_ADD
          || type == Command::MATERIAL_DELETE) {
    int cur = materialchooser->value();
    MaterialList *mlist = app->materialList;
    materialchooser->clear();
    for (int m=0; m<mlist->num(); m++) {
      materialchooser->add(mlist->material_name(m));
    }
    if (type != Command::MATERIAL_DELETE) {
      materialchooser->value(cur);
    } else {
      if (molindex >= 0 && repindex >= 0) {
        Molecule *mol = app->moleculeList->molecule(molindex);
        DrawMolItem *d = mol->component(repindex);
        materialchooser->value(d->curr_material());
      } else {
        materialchooser->value(0);
      }
    }
  } else if (type == Command::MOL_REPSELUPDATE ||
             type == Command::MOL_REPCOLORUPDATE) {
    update_rep();
  } else if (type == Command::ATOMSEL_ADDMACRO || 
           type == Command::ATOMSEL_DELMACRO) {
    selbuilder->update_macrobrowser();

  } else if (type == Command::MOL_SHOWPERIODIC ||
             type == Command::MOL_NUMPERIODIC) {
    update_pbc();
  } else {
    return FALSE;
  }
  return TRUE;
}


int GraphicsFltkMenu::selectmol(int selmol) {
  if (selmol >= 0 && selmol < molchooser->size()-1) 
    molindex = selmol;
  update_molchooser();
  return TRUE;
}

